package com.cs410.collabwriting.tools.queryTools;

import java.util.ArrayList;
import java.util.LinkedList;

import com.cs410.collabwriting.tools.DatabaseManager;
import com.cs410.collabwriting.tools.LogManager;



public class QueryBuilder {

	
	private String m_query;
	private boolean m_isDistinct = false;
	private boolean m_cascade = true;
	private ArrayList<String> m_columns;
	private ArrayList<String> m_tables;
	private ArrayList<String> m_conditions;
	private ArrayList<String> m_additionalCriteria;
	private ArrayList<String> m_values;
	private LinkedList<Object> m_variables;
	private int m_conditionIndex = 0;
	private QueryType m_queryType;
	
	private QueryBuilder() {
		initializeVariables();
	}
	
	public QueryBuilder( QueryType type ) {
		m_queryType = type;
		initializeVariables();
	}
	
	public QueryBuilder addColumns( String...columnNames ) {
		for( String columnName : columnNames ) {
			m_columns.add( columnName );
		}
		return this;
	}
	
	public QueryBuilder addTables( String...tableNames ) {
		for( String tableName : tableNames ) {
			m_tables.add( tableName );
		}
		return this;
	}
	
	// String input must be "AND <condition>" or "OR <condition>"
	public QueryBuilder addConditions( String condition, String columnName, String operator, String value ) {    
	    String whereClause = "";
		if( m_conditions.size() > 0 ) {
			whereClause += condition;
		}
		whereClause += columnName;
		whereClause += operator;
		if( value.contains(".") ) {
			if( DatabaseManager.isValidDatabaseTableAndColumn( value ) ) {
				whereClause += value;
			} else {
				whereClause += " ? ";
				m_variables.add( value );
			}
		} else {
			whereClause += " ? ";
			m_variables.add( value );
		}
		m_conditions.add( whereClause );
		return this;
	}
	
	public QueryBuilder addConditions( String condition, String columnName, String operator, int value ) {
        String whereClause = "";
		if( m_conditions.size() > 0 ) {
			whereClause += condition;
		}
		whereClause += columnName;
		whereClause += operator;
		whereClause += " ? ";
		m_variables.add(value);
		
		m_conditions.add( whereClause );
		return this;
	}
	
	public QueryBuilder addAdditionalCriteria( String...criterias ) {
		for( String criteria : criterias ) {
			m_additionalCriteria.add( criteria );
		}
		return this;
	}
	
	public LinkedList<Object> getVariables() {
		return m_variables;
	}
	
	public QueryBuilder addValues( String...values) {
		for( String value : values ) {
			m_variables.add( m_conditionIndex++, value );
			m_values.add( " ? " );
			
		}
		return this;
	}
	
	public QueryBuilder setQueryType( QueryType type ) {
		m_queryType = type;
		return this;
	}
	
	public QueryBuilder setDistinct( boolean distinct ) {
		m_isDistinct = distinct;
		return this;
	}
	
	public QueryBuilder setCascade( boolean cascade ) {
		m_cascade = cascade;
		return this;
	}
	
	public static String toSQLChar( String input ) {
		return input;
	}
	
	public QueryBuilder value() {
		return this;
	}
	
	public String toString() {
		switch( m_queryType ) {
			case SELECTION: m_query = selectionBuilder(); break;
			case INSERT: m_query = insertionBuilder(); break;
			case UPDATE: m_query = updateBuilder(); break;
			case DROP: m_query = dropTableBuilder(); break;
			case DELETE: m_query = deleteBuilder(); break;
			default: m_query = selectionBuilder(); break;
		};
		LogManager.logDebug("[QueryBuilder] SQLQuery: " + m_query, LogManager.LOG_LEVEL_3 );
		return m_query;
	}
	
	private void initializeVariables() {
		m_query = "";
		m_columns = new ArrayList<String>();
		m_tables = new ArrayList<String>();
		m_conditions = new ArrayList<String>();
		m_additionalCriteria = new ArrayList<String>();
		m_values = new ArrayList<String>();
		m_variables = new LinkedList<Object>();
	}
	
	private String selectionBuilder() {
		String selectQuery = "SELECT ";
		
		if( m_isDistinct ) {
			selectQuery += "DISTINCT ";
		}
		
		if( m_columns.isEmpty() ) {
			selectQuery += " * ";
		} else {
			for( int i = 0; i < m_columns.size(); i++ ) {
				selectQuery += (m_columns.get( i ) + " ");
				if( i + 1 < m_columns.size() ) {
					selectQuery = selectQuery.concat(" , ");
				}
			}
		}
		
		selectQuery += " FROM ";
		for( int i = 0; i < m_tables.size(); i++ ) {
			selectQuery += (m_tables.get( i )  + " " );
			if( i + 1 < m_tables.size() ) {
				selectQuery = selectQuery.concat(" , ");
			}
		}
			
		if( !m_conditions.isEmpty())
			selectQuery += " WHERE ";
		
		for( int i = 0; i < m_conditions.size(); i++ ) {
			selectQuery = selectQuery.concat( (m_conditions.get( i ).concat(" ")));
		}
		
		for( int i = 0; i < m_additionalCriteria.size(); i++ ) {
			selectQuery = selectQuery.concat((m_additionalCriteria.get( i ).concat(" ")));
		}
		return selectQuery;
	}
		
	// will only insert one table, 
	private String insertionBuilder() {
		String query = "INSERT INTO ";
		
		query += (m_tables.get( 0 ) + "(");
		for( int i = 0; i < m_columns.size() && i < m_values.size() ; i++ ) {
			query += m_columns.get( i );
			if( i + 1 < m_columns.size() && i + 1 < m_values.size() ) {
				query = query.concat(",");
			}
		}
		query += ") VALUES (";
		
		for( int i = 0; i < m_values.size() && i < m_values.size() ; i++ ) {
			query += m_values.get( i );
			if( i + 1  < m_values.size() && i + 1 < m_values.size() ) {
				query = query.concat(",");
			}
		}
		query += ")";
		return query;
	}

	private String updateBuilder() {
		String query = "UPDATE ";
		query += (m_tables.get( 0 ) + " SET ");
		
		for(int i = 0; i < m_columns.size() && i < m_values.size(); i++ ) {
			query += ( m_columns.get( i ) + " = " + m_values.get( i ) + " ");
			if( i + 1 < m_columns.size() && i + 1 < m_values.size() ) {
				query = query.concat(", ");
			}
		}
		
		if( !m_conditions.isEmpty() ) {
			query += " WHERE ";
			for( int i = 0; i < m_conditions.size(); i++ ) {
				query = query.concat(m_conditions.get( i ).concat(" "));
			}
		}		
		return query;
	}

	private String dropTableBuilder() {
		String query = "DROP TABLE ";
		query += m_tables.get( 0 );
		
		if( m_cascade ) {
			query += " CASCADE ";
		}
		return query;
	}

	private String deleteBuilder() {
		String query = "DELETE FROM ";
		query += (m_tables.get( 0 ) + " WHERE ");
		
		for( int i = 0; i < m_conditions.size(); i++ ) {
			query = query.concat(( m_conditions.get( i ).concat(" ")));
		}
		return query;
	}

}
